using System;
using System.Reflection;
using System.Configuration;
using System.Configuration.Provider;

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

using System.Security;
using System.Security.Principal;

namespace CSharpDistilled.Diagnostics.Providers
{
    /// <summary>
    /// Defines the contract that allows for logging through one or more custom logging providers. 
    /// </summary>
    public abstract class LoggingProviderBase : ProviderBase
    {
        #region LoggingProviderBase Private Fields

        private LoggingContext m_context;

        #endregion

        #region protected LoggingProviderBase()

        /// <summary>
        /// Initializes a new instance of the <see cref="T:LoggingProviderBase"/> class.    
        /// </summary>
        protected LoggingProviderBase()
        {

        }

        #endregion

        #region protected LoggingContext LoggingContext

        /// <summary>
        /// Gets or sets the <see cref="T:CSharpDistilled.Diagnostics.LoggingContext"/> containing details regarding the log entry to be published.
        /// </summary>
        protected LoggingContext LoggingContext
        {
            get
            {
                return m_context;
            }
            set
            {
                m_context = value;
            }
        }

        #endregion

        #region public static LoggingProviderBase CreateInstance()

        /// <summary>
        /// Creates an instance of the default logging provider.
        /// </summary>
        /// <returns>An instance of the default logging provider.</returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static LoggingProviderBase CreateInstance()
        {
            try
            {
                // retrieve the logging provider configuration.
                ProviderConfiguration configuration = ProviderConfiguration.GetConfiguration();

                // retrieve an instance of the default provider.
                Provider provider = configuration.LoggingProviders[configuration.DefaultLoggingProvider] as Provider;

                #region Trace.Write()

                Trace.Write("LoggingProviderBase.CreateInstance() - Default Logging Provider = ");
                Trace.WriteLine(provider.Name);
                Trace.Flush();

                #endregion

                // establish the System.Type for the default provider.
                Type providerType = Type.GetType(provider.Type);

                return Activator.CreateInstance(providerType) as LoggingProviderBase;
            }
            catch(Exception ex)
            {
                Trace.WriteLine(ex.Message);
                Trace.WriteLine(ex.StackTrace);

                // no logging provider configuration could be found or specified provider was not found, return the default provider.
                return new SqlLoggingProvider() as LoggingProviderBase;
            }
        }

        #endregion

        #region public abstract void Log(LoggingContext context)

        /// <summary>
        /// Publishes the specified <see cref="T:CSharpDistilled.Diagnostics.LoggingContext"/> using the configured logging provider.
        /// </summary>
        /// <param name="context">The <see cref="T:CSharpDistilled.Diagnostics.LoggingContext"/> to be published using the configured logging provider.</param>
        public abstract void Log(object context);

        #endregion

        #region public abstract void Log(string entry, LoggingPriority priority, LoggingType type, IIdentity identity)

        /// <summary>
        /// Publishes the specified <see cref="T:System.String"/> using the configured logging provider.
        /// </summary>
        /// <param name="entry">The <see cref="T:System.String"/> to be published.</param>
        /// <param name="priority">The <see cref="T:CSharpDistilled.Diagnostics.LoggingPriority"/> of the specified log entry.</param>
        /// <param name="type">The <see cref="T:CSharpDistilled.Diagnostics.LoggingType"/> of the specified log entry.</param>
        /// <param name="identity">The <see cref="T:System.Security.Principal.IIdentity"/> to associate with this log entry.</param>
        public abstract void Log(string entry, LoggingPriority priority, LoggingType type, IIdentity identity);

        #endregion
    }
}
